
/**
 * 原生拖拽 API 实现拖拽窗口定位效果
 */

/**
 * 使得一些元素可被拖动，并放置到指定元素区域内定位，实现窗口拖拽效果
 * @param dragElements - 拖拽元素数组
 * @param putAreaElement - 放置区域元素
 */
export default function initDraggableArea(dragElements: HTMLElement[], putAreaElement: HTMLElement) {
    let currentDragElement: HTMLElement;

    dragElements.forEach(item => {
        // 设置元素可被拖动
        item.draggable = true;

        item.addEventListener("dragstart", (e: DragEvent) => {
            currentDragElement = e.target as HTMLElement;

            // 记录鼠标在当前元素中的相对坐标，并将其存入数据传输对象中
            const { offsetX, offsetY } = e;
            e.dataTransfer?.setData("offsetX", `${offsetX}`);
            e.dataTransfer?.setData("offsetY", `${offsetY}`);

            e.dataTransfer!.effectAllowed = "move";

            // 拖拽后在原位置隐藏当前元素
            // 跟随鼠标元素的样式就是原始元素的样式加上半透明的效果
            // 因此如果立马修改原始元素样式，就会将吸附在鼠标上的元素的样式也改掉，所以通过宏任务延迟一下修改
            setTimeout(() => (currentDragElement.style.opacity = "0"), 0);
            // 修改拖拽时鼠标上的快照图像
            // e.dataTransfer!.setDragImage(img, 0, 0);
        });

        item.addEventListener("dragend", () => {
            // 拖拽完毕后恢复原状
            currentDragElement.style.opacity = "1";
        });
    });

    // 阻止默认的重置行为，即可成为拖拽元素的放置区
    putAreaElement.addEventListener("dragenter", e => e.preventDefault());
    putAreaElement.addEventListener("dragover", e => e.preventDefault());

    putAreaElement.addEventListener("drop", e => {
        // 获取鼠标在放置时相对放置元素左上角的坐标
        const { offsetX: x, offsetY: y } = e;

        const offsetX = e.dataTransfer!.getData("offsetX");
        const offsetY = e.dataTransfer!.getData("offsetY");

        const left = x - +offsetX;
        const top = y - +offsetY;

        const maxLeft = putAreaElement.offsetWidth - currentDragElement.offsetWidth;
        const maxTop = putAreaElement.offsetHeight - currentDragElement.offsetHeight;

        putAreaElement.style.position = "relative";
        currentDragElement.style.position = "absolute";

        // 设置放置位置：计算被拖拽元素在放置时相对放置元素左上角的坐标
        currentDragElement.style.left = `${Math.min(Math.max(left, 0), maxLeft)}px`;
        currentDragElement.style.top = `${Math.min(Math.max(top, 0), maxTop)}px`;

        putAreaElement.append(currentDragElement);
    });
}

